///
/// ArmyBodger 3
/// (c)2010 richyp
///
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Xml.Linq;
using System.Xml.XPath;
using ArmyBodger.Core;

namespace ArmyBodger.UI {
    /// <summary>
    /// The Main Form of ArmyBodger
    /// </summary>
    public partial class BodgerForm : Form {
        /// <summary>
        /// Default Constructor
        /// </summary>
        public BodgerForm() {
            InitializeComponent();
            this.GameSystemParser = new GameSystemParser();
            this.SaveFileParser = new SaveFileParser();
            this.GameSystems = new GameSystemCollection();
            this.GameSystemsForm = new BodgerGameSystemsForm();
            this.ArmyBookParser = new ArmyBookParser(this.GameSystems);
            this.printDocument = new ListPrintDocument();
            this.ScriptCore = ScriptCore.Instance;           
            this.ScriptCore.AddVariable("MainForm", this);
            this.ScriptCore.AddVariable("List", this.entryListControl);
            this.ScriptCore.AddVariable("PluginMenu", this.scriptsToolStripMenuItem);
            this.ScriptCore.AddVariable("PrintDocument", this.printDocument);
            this.ScriptTest = new ScriptEngineForm(this.ScriptCore);
            this.GameSystemParser.ScriptCore = this.ScriptCore;
            this.Startup();
            this.entryListControl.Form = this;
            this.entryListControl.ScriptCore = this.ScriptCore;
            MainForm = this;
            this.ShowEquipmentPanel();
        }
        #region Properties
        /// <summary>
        /// Static reference to self so that the ScriptCore can interact with it 
        /// directly
        /// </summary>
        public static BodgerForm MainForm { get; set; }
        /// <summary>
        /// The ArmyBookParser object
        /// </summary>
        public ArmyBookParser ArmyBookParser { get; set; }
        /// <summary>
        /// The ArmyBook currently being used
        /// </summary>
        public ArmyBook CurrentArmyBook { get; set; }
        /// <summary>
        /// The SaveFileParser object
        /// </summary>
        public SaveFileParser SaveFileParser { get; set; }
        /// <summary>
        /// The GameSystemParser object
        /// </summary>
        public GameSystemParser GameSystemParser { get; set; }
        /// <summary>
        /// Collection of supported GameSystems
        /// </summary>
        public GameSystemCollection GameSystems { get; set; }
        /// <summary>
        /// The supported Game Systems are shown on this form
        /// </summary>
        public BodgerGameSystemsForm GameSystemsForm { get; set; }
        /// <summary>
        /// Create a new list using this form
        /// </summary>
        public CreateNewListForm CreateListForm { get; set; }
        /// <summary>
        /// The ScriptCore object (Singleton)
        /// </summary>
        public ScriptCore ScriptCore { get; set; }
        /// <summary>
        /// A window that allows direct entry of Python code
        /// </summary>
        protected ScriptEngineForm ScriptTest { get; set; }
        /// <summary>
        /// Print Preview Form
        /// </summary>
        protected ListPrintDocument printDocument { get; set; }
        /// <summary>
        /// Get the equipment panel
        /// </summary>
        public EquipmentPanel EquipmentPanel {
            get {
                return this.equipmentPanel;
            }
        }
        public int PointsLevel
        {
            get
            {
                int totalPoints;
                if (!int.TryParse(this.pointsBox.Text, out totalPoints))
                    totalPoints = 2000;
                totalPoints = Math.Max(1, totalPoints);
                return totalPoints;
            }
        }
        #endregion
        #region Methods
        /// <summary>
        /// Create a new list
        /// </summary>
        /// <param name="armyBook">The army book used for this list</param>
        public void CreateNewList(string armyBook) {
            this.CurrentArmyBook = this.ArmyBookParser.ParseArmyBookFile(
             Properties.Settings.Default.ArmyBooksPath,
             armyBook,
             Properties.Settings.Default.TempDirectory
             );
            this.ScriptCore.AddVariable("ArmyBook", this.CurrentArmyBook);
            foreach (string scriptFile in this.CurrentArmyBook.ScriptFiles) {
                this.ScriptCore.LoadFile(scriptFile);
            }
            this.entryListControl.Clear();
            this.PopulateChoices();
        }
        /// <summary>
        /// Fill the entry list with possible entries from the current armybook
        /// </summary>
        public void PopulateChoices() {
            this.listViewEntries.Items.Clear();
            this.listViewEntries.BeginUpdate();
            List<string> categories = new List<string>();
            foreach (Entry choice in this.CurrentArmyBook.AvailableEntries) {
                if (!categories.Contains(choice.Category))
                {
                    categories.Add(choice.Category);
                    ListViewItem category = new ListViewItem(choice.Category.Substring(2));
                    category.Font = new Font(category.Font, FontStyle.Bold);
                    category.ForeColor = Color.Crimson;
                    this.listViewEntries.Items.Add(category);
                }

                Entry defaultEntry = choice.CreateDefaultEntry();
                ListViewItem item = new ListViewItem(
                    new string[] { defaultEntry.Name, defaultEntry.Cost.ToString()}
                    );
                if(choice.IsSpecial)
                    item.ForeColor = Color.RoyalBlue;

                item.Tag = choice;
                this.listViewEntries.Items.Add(item);
            }
            this.listViewEntries.EndUpdate();
            this.saveAsToolStripMenuItem.Enabled = true;
        }
        /// <summary>
        /// Get a reference to the main list control
        /// </summary>
        /// <returns></returns>
        public EntryListControl GetListControl() {
            return this.entryListControl;
        }
        /// <summary>
        /// Executed when ArmyBodger is loaded
        /// </summary>
        public void Startup() {
            string gameSystemPath = Properties.Settings.Default.GameSystemsPath;
            foreach (string folder in Directory.GetDirectories(gameSystemPath)) {
                foreach (string file in Directory.GetFiles(folder)) {
                    if (file.EndsWith("index.xml")) {
                        GameSystem gs = this.GameSystemParser.ParseGameSystemPath(folder);
                        GameSystems.Add(gs);
                        this.statusBarLabel.Text = "Loaded Game System: " + gs.Header.Name;
                    }
                }
            }
            GameSystemsForm.UpdateTable(GameSystems);
            this.CreateListForm = new CreateNewListForm(this.GameSystems);
            this.LoadPlugins();
        }
        /// <summary>
        /// Load all the plugins from the plugin directory
        /// </summary>
        public void LoadPlugins() {
            int index = 0;                    
            foreach (string file in Directory.GetFiles(Properties.Settings.Default.PluginsDirectory)) {
                if (file.ToUpper().EndsWith(".PY")) {
                    StringBuilder content = new StringBuilder();
                    StreamReader reader = new StreamReader(file);
                    string line = null;
                    while ((line = reader.ReadLine()) != null) {
                        line = line.Replace("RunPlugin", "RunPlugin" + index);
                        content.Append(line + "\n");
                    }
                    this.ScriptCore.Execute(content.ToString());
                    this.ScriptCore.Execute("RunPlugin" + index + "()");
                    index++;
                }
            }
        }
        /// <summary>
        /// Display a print preview of the current list
        /// </summary>
        public void PrintPreview() {
            if (this.entryListControl.GetEntries().Count > 0) {
                this.printDocument.ArmyBook = this.CurrentArmyBook;
                this.printDocument.Entries = this.entryListControl.GetEntries();
                this.printPreviewDialog.Document = this.printDocument;
                this.printPreviewDialog.ShowDialog(this);
            }
        }
        /// <summary>
        /// Display the new EquipmentPanel (experimental)
        /// </summary>
        public void ShowEquipmentPanel() {
            this.splitContainer1.Panel2Collapsed = false;
        }
        /// <summary>
        /// Display the new EquipmentPanel (experimental)
        /// </summary>
        public void HideEquipmentPanel() {
            this.splitContainer1.Panel2Collapsed = true;
        }
        /// <summary>
        /// Load a list file
        /// </summary>
        /// <param name="filename">The file to load</param>
        public void OpenList(string filename) {
            XDocument doc = XDocument.Load(filename);
            string gameSystem = doc.XPathSelectElement("list/gameSystem").Value;
            string armyBook = doc.XPathSelectElement("list/armyBook").Value;
            if (this.GameSystems[gameSystem] != null) {
                this.CreateNewList(gameSystem + "/" + armyBook + ".zip");
                GameSystem gs = this.GameSystems[gameSystem];
                ArmyBook ab = this.CurrentArmyBook;
                EntryCollection entries = this.SaveFileParser.Load(ab, doc, gs);
                foreach (Entry entry in entries) {
                    this.entryListControl.AddEntry(entry);
                }
                this.entryListControl.DeselectAll();
                this.entryListControl.Render();
            } else {
                MessageBox.Show(
                    this,
                    "No such game system: " + gameSystem,
                    "Invalid Game System",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error
                    );

            }
        }
        #endregion
        #region EventHandlers
        /// <summary>
        /// Show Game Systems clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void supportedGameSystemsToolStripMenuItem_Click(object sender, EventArgs e) {
            this.GameSystemsForm.ShowDialog(this);
        }
        /// <summary>
        /// Create New List clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void newToolStripMenuItem_Click(object sender, EventArgs e) {
            DialogResult dr = this.CreateListForm.ShowDialog(this);
            if (dr == DialogResult.OK && this.CreateListForm.SelectedArmyBook != null) {
                this.CurrentArmyBook = this.CreateListForm.SelectedArmyBook;
                this.PopulateChoices();
                foreach (string scriptFile in this.CurrentArmyBook.ScriptFiles) {
                    ScriptCore.Instance.LoadFile(scriptFile);
                }
                this.armyNameBox.Text = this.CreateListForm.ArmyName;
                this.pointsBox.Text = this.CreateListForm.PointsLevel.ToString();
                this.entryListControl.Clear();
            }
        }
        /// <summary>
        /// Script Test... clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void scriptTestToolStripMenuItem_Click(object sender, EventArgs e) {
            this.ScriptTest.Show(this);
        }

        /// <summary>
        /// An entry has been double clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void listViewEntries_ItemActivate(object sender, EventArgs e)
        {
            if ((listViewEntries.SelectedItems[0].Tag) == null)
                return;

            Entry entry = ((Entry)listViewEntries.SelectedItems[0].Tag).CreateDefaultEntry();

            if (!String.IsNullOrEmpty(entry.OnAddEvent))
            {
                this.ScriptCore.Execute(entry.OnAddEvent);
            }

            this.entryListControl.AddEntry(entry);
        }
        
        /// <summary>
        /// An entry has been dragged from the possible choices list
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void listViewEntries_ItemDrag(object sender, ItemDragEventArgs e) {
            this.DoDragDrop(
                (Entry)((ListViewItem)e.Item).Tag,
                DragDropEffects.Copy
                );
        }

        /// <summary>
        /// The dragged entry has entered the list control
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void entryListControl_DragEnter(object sender, DragEventArgs e) {
            if(e.Data.GetData(typeof(Entry)) != null ) {
                e.Effect = DragDropEffects.Copy;
            }
        }
        /// <summary>
        /// The dragged entry has been dropped on the list control
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void entryListControl_DragDrop(object sender, DragEventArgs e) {
            if (e.Data.GetData(typeof(Entry)) != null) {
                Entry entry = ((Entry)e.Data.GetData(typeof(Entry))).CreateDefaultEntry();
                if (!String.IsNullOrEmpty(entry.OnAddEvent)) {
                    this.ScriptCore.Execute(entry.OnAddEvent);
                }
                this.entryListControl.AddEntry(entry);
                this.entryListControl.SelectedEntry = entry;
            }
        }       
        /// <summary>
        /// Exit was clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void exitToolStripMenuItem_Click(object sender, EventArgs e) {
            Application.Exit();
        }
        /// <summary>
        /// Save As... clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e) {
            this.saveFileDialog.DefaultExt = ".abf";
            this.saveFileDialog.FileName = this.CurrentArmyBook.Header.Name + ".abf";
            DialogResult dr = this.saveFileDialog.ShowDialog(this);
            if (dr == DialogResult.OK) {
                this.SaveList(this.saveFileDialog.FileName);
            }
        }
        public void SaveList(string filename) {
            StreamWriter writer = new StreamWriter(filename);
            writer.WriteLine(this.entryListControl.ToXml());
            writer.Flush();
            writer.Close();
            this.Text = this.saveFileDialog.FileName;
        }
        /// <summary>
        /// Open... clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void openToolStripMenuItem_Click(object sender, EventArgs e) {
            DialogResult dr = this.openFileDialog.ShowDialog(this);
            if (dr == DialogResult.OK) {
                this.OpenList(this.openFileDialog.FileName);
            }
        }
        /// <summary>
        /// Print Preview was clicked
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void printPreviewToolStripMenuItem_Click(object sender, EventArgs e) {
            this.PrintPreview();
        }
        #endregion
    }
}
